package beautiful.butterfly.drds.binlog.handlers;


import beautiful.butterfly.drds.binlog.command.CheckBinlogCheckSumCommand;
import beautiful.butterfly.drds.binlog.command.RequestBinaryLogCommand;
import beautiful.butterfly.drds.binlog.connection.ConnectionAttributes;
import beautiful.butterfly.drds.binlog.netty.server.NioSocketChannelExtend;
import beautiful.butterfly.drds.binlog.packet.RowPacket;
import beautiful.butterfly.drds.binlog.type.CheckSumType;
import beautiful.butterfly.drds.binlog.utils.ByteUtils;
import beautiful.butterfly.drds.binlog.utils.Constants;
import beautiful.butterfly.drds.binlog.utils.Nettys;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;

@Slf4j
public class FetchBinlogCheckSumResultChannelInboundHandler extends SimpleChannelInboundHandler<ByteBuf>
{

    private State currentState = State.NONE;
    private ArrayList<RowPacket> rowPackets = new ArrayList<RowPacket>();
    private CheckSumType checkSumType = CheckSumType.none;

    public FetchBinlogCheckSumResultChannelInboundHandler()
    {

    }

    @SuppressWarnings("incomplete-switch")
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception
    {

        //
        try
        {
            // LoggerUtils.debug(logger,
            // "进入FetchBinlogChecksumResultHandler处理器");
            // LoggerUtils.debug(logger, "state---" + currentState.name());
            // 会陆续收到一些完整的字符报文
            // 需要根据当前状态决定操作
            short firstByte;

            switch (currentState)
            {
                case NONE:
                    // 成功了,切换状态
                    currentState = State.WAITING_FOR_BEGIN;
                    break;
                case WAITING_FOR_BEGIN:
                    firstByte = ByteUtils.readUnsignedByte(byteBuf);
                    if (0x00FE != firstByte)
                    {
                        // 收到了一个无效的,skip
                    } else
                    {
                        // 接收到了开始信号
                        currentState = State.WAITING_FOR_END;
                    }
                    break;
                case WAITING_FOR_END:
                    // 在此过程中有效的报文是我们要的
                    byteBuf.markReaderIndex();
                    firstByte = ByteUtils.readUnsignedByte(byteBuf);
                    if (0x00FE != firstByte)
                    {
                        // 有效的报文
                        // 重置下
                        byteBuf.resetReaderIndex();// 恢复
                        RowPacket rowPacket = new RowPacket(byteBuf);
                        rowPackets.add(rowPacket);
                    } else
                    {
                        // @@@@@@@@@@@@@@@@@@@@@
                        // 结束了
                        currentState = State.END;
                        if (rowPackets.size() > 0)
                        {// 尽最大努力保存现场情况，然后肯定要退出context
                            // 只需要第一个
                            checkSumType = CheckSumType.valueOf(rowPackets.get(0).getValues(1).toUpperCase());
                        } else
                        {
                            checkSumType = CheckSumType.none;
                        }

                        // checkSumType之后，有可能进入第二阶段
                        if (CheckSumType.none != checkSumType)
                        {
                            new CheckBinlogCheckSumCommand("set @master_binlog_checksum= @@global.binlog_checksum")
                                    .write(channelHandlerContext);
                            currentState = State.SECOND_STAGE;
                        } else
                        {
                            // 立即发送请求二进制日志命令

                            ConnectionAttributes connectionAttributes = ((NioSocketChannelExtend) channelHandlerContext.channel()).getConnectionAttributes();
                            String name = connectionAttributes.getBinlogFileName();
                            long position = connectionAttributes.getBinlogFilePosition();
                            new RequestBinaryLogCommand(connectionAttributes.getClientId(), name, position).write(channelHandlerContext);
                            log.info("RequestBinaryLogCommand: " + name + ":" + position);
                            // 这里要做一个变换
                            channelHandlerContext.pipeline().remove(this);// 完成使命，退出历史舞台
                            channelHandlerContext.pipeline().removeFirst();// 把第一个删除
                            channelHandlerContext.pipeline().addFirst(Constants.FIXED_LENGTH_HANDLER_V2, new FixedLengthV2ChannelInboundHandler());
                        }
                        // @@@@@@@@@@@@@@@@@@@@@
                    }
                    break;
                case SECOND_STAGE:
                    // 进入第二阶段,表明接受了checksum,恭喜
                    // 成功进入,则需要设置序列化为本checksum.
                    // LoggerUtils.info(logger, "succeed to check checksum: " +
                    // checkSumType);
                    // 保留checkSumType
                    ConnectionAttributes connectionAttributes = ((NioSocketChannelExtend) channelHandlerContext.channel()).getConnectionAttributes();
                    connectionAttributes.setCheckSumType(checkSumType);
                    //
                    // 立即发送请求二进制日志命令
                    String binlogFileName = connectionAttributes.getBinlogFileName();
                    long binlogFilePosition = connectionAttributes.getBinlogFilePosition();
                    new RequestBinaryLogCommand(connectionAttributes.getClientId(), binlogFileName, binlogFilePosition).write(channelHandlerContext);
                    log.info("RequestBinaryLogCommand: " + binlogFileName + ":" + binlogFilePosition);
                    // 这里要做一个变换
                    channelHandlerContext.pipeline().remove(this);// 完成使命，退出历史舞台
                    channelHandlerContext.pipeline().removeFirst();// 把第一个删除
                    channelHandlerContext.pipeline().addFirst(Constants.FIXED_LENGTH_HANDLER_V2, new FixedLengthV2ChannelInboundHandler());
                    break;
            }
        } catch (Exception e)
        {
            log.info(e.toString());
            throw new Exception(e);
        }

    }

    ;

    @Override
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable throwable)
    {
        // Close the connection when an exception is raised.
        // cause.printStackTrace();//务必要关闭
        log.info("Exception:" + throwable.toString());
        Nettys.cleanChannelContext(channelHandlerContext, throwable);
    }

    @Override
    public void channelInactive(ChannelHandlerContext channelHandlerContext) throws Exception
    {
        log.info("[channelInactive] socket is closed by remote server");
        Nettys.cleanChannelContext(channelHandlerContext, null);
    }

    private enum State
    {
        NONE, // 一个都没收到
        WAITING_FOR_BEGIN, // 收到了第一个
        WAITING_FOR_END, // 接收我们需要的报文
        END, SECOND_STAGE// 第二阶段
    }

}
